Skip to content

GH-50915: [FORMAT] Allow TIMESTAMP logical type to annotate FIXED_LEN_BYTE_ARRAY(12) - #50916

Open
divjotarora wants to merge 3 commits into
apache:mainfrom
divjotarora:flba-12
Open

GH-50915: [FORMAT] Allow TIMESTAMP logical type to annotate FIXED_LEN_BYTE_ARRAY(12)#50916
divjotarora wants to merge 3 commits into
apache:mainfrom
divjotarora:flba-12

Conversation

@divjotarora

@divjotarora divjotarora commented Aug 19, 2026

Copy link
Copy Markdown

Rationale for this change

See apache/parquet-format#600 for rationale.

What changes are included in this PR?

This PR adds support for using TimestampType to annotate FIXED_LEN_BYTE_ARRAY(12) values.

Are these changes tested?

Yes, via unit tests and an e2e test that reads the file added in parquet-testing (apache/parquet-testing#123).

Are there any user-facing changes?

No

@github-actions

Copy link
Copy Markdown

⚠️ GitHub issue #50915 has been automatically assigned in GitHub to PR creator.

Comment thread cpp/src/parquet/arrow/schema_internal.cc
Comment thread cpp/src/parquet/reader_test.cc
Comment thread cpp/src/parquet/statistics.cc Outdated

@emkornfield emkornfield left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some questions and comments I think the biggest one is scope and whether we should have an option to convert this value to a proper arrow type. Wemight also want to make it configurable the target of the arrow type

@github-actions github-actions Bot added the awaiting review Awaiting review label Aug 20, 2026
Comment thread cpp/src/parquet/arrow/arrow_reader_writer_test.cc
@github-actions github-actions Bot added awaiting committer review Awaiting committer review and removed awaiting review Awaiting review labels Sep 3, 2026
return Status::Invalid(
"FLBA(12) TIMESTAMP value does not fit in a 64-bit Arrow timestamp");
}
const bool negative = (bytes[11] & 0x80) != 0;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not re-use low_signed and do a copmarison against it?

const uint32_t high = bit_util::FromLittleEndian(SafeLoadAs<uint32_t>(bytes + 8));
const int64_t low_signed = static_cast<int64_t>(low);
// Fits in int64 iff the high part is a pure sign-extension of the low part.
if (static_cast<int32_t>(high) != (low_signed < 0 ? -1 : 0)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: separate (low_signed < 0 ? -1 : 0) to its own variable 'sign_extension'


return ::arrow::fixed_size_binary(physical_length);
case LogicalType::Type::TIMESTAMP:
// If configured, convert to a potentially lossy Arrow timestamp. Otherwise, return

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: maybe simplify the comment to just note this path is loss, otherwise I think the logic is pretty much self documenting?

should_load_statistics_(false),
smallest_decimal_enabled_(false) {}
smallest_decimal_enabled_(false),
convert_flba_timestamps_(false),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is probably more useful to default this to true, most timestamps will likely be in a range that is handle-able, and if the intent is for it to be timestamp then I think this makes a better end-user experience. CC @wgtmac @pitrou do you have a preference here?

Keeping the clamp_on_overflow to false makes sense to me because otherwise it is potential corruption/data-loss.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM

bool clamp_on_overflow) {
static const auto binary_type = ::arrow::fixed_size_binary(12);
std::shared_ptr<ChunkedArray> chunked_array;
RETURN_NOT_OK(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need to transfer here, can't we read with the underlying reader?

const bool negative = (bytes[11] & 0x80) != 0;
builder.UnsafeAppend(negative ? INT64_MIN : INT64_MAX);
} else {
builder.UnsafeAppend(low_signed);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we probably need to multiply/divide based on the logical arrow type and logical parquet type? Maybe this is an argument to limit the FLBA type to nanosecond and finer granularities when they exist?

@emkornfield emkornfield left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the conversion code always assumes a 1:1 mapping between arrow timestamp granularity and parquet granularity. I think in the common path when schema is inferred this is workable, but IIRC users can also supply there own schema (we should add test coverage for this path).

/// \brief Set whether to infer Arrow timestamps from Parquet FLBA types.
///
/// When enabled, Parquet FLBA(12) TIMESTAMP columns are read as Arrow timestamps.
/// VAlues that do not fit in 64 bit timestamps are handled per

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// VAlues that do not fit in 64 bit timestamps are handled per
/// Values that do not fit in 64 bit timestamps are handled per

should_load_statistics_(false),
smallest_decimal_enabled_(false) {}
smallest_decimal_enabled_(false),
convert_flba_timestamps_(false),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants